package org.llc.authorization.service;

import org.llc.authorization.config.util.GenerateAuthorizationCode;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 授权码存取策略
 *
 * <p>重写授权码存取策略 JdbcAuthorizationCodeServices替换
 * 授权码缓存redis里面，RedisAuthorizationCodeServices，解决启动多个认证中心授权码问题
 *
 * @author llc
 * @date 2019/5/31 11:03
 * @since 1.0.0
 */
@Component
public class RedisAuthorizationCodeServices extends RandomValueAuthorizationCodeServices {

    private RedisTemplate<String, Object> redisTemplate;

    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }


    /**
     * 使用redis替换jdbc存储策略
     *
     * <p>替换JdbcAuthorizationCodeServices的存储策略
     * 存储code到redis，并设置过期时间，10分钟<br>
     * value为OAuth2Authentication序列化后的字节<br>
     * 因为OAuth2Authentication没有无参构造函数<br>
     * redisTemplate.opsForValue().set(key, value, timeout, unit);
     * 这种方式直接存储的话，redisTemplate.opsForValue().get(key)的时候有些问题，
     * 所以这里采用最底层的方式存储，get的时候也用最底层的方式获取
     *
     * @author llc
     * @date 2019/5/31 11:03
     */
    @Override
    protected void store(String code, OAuth2Authentication authentication) {
        redisTemplate.opsForValue().set(redisKey(code), authentication, 10, TimeUnit.MINUTES);
    }

    /**
     * 删除使用过的code
     *
     * @param code 授权码
     * @return org.springframework.security.oauth2.provider.OAuth2Authentication
     * @author llc
     * @date 2020/1/17 17:54
     */
    @Override
    protected OAuth2Authentication remove(final String code) {

        String codeKey = redisKey(code);

        OAuth2Authentication token = (OAuth2Authentication) redisTemplate.opsForValue().get(codeKey);

        this.redisTemplate.delete(codeKey);

        return token;
    }

    /**
     * redis中 code key的前缀
     *
     * @param code redis中 code key的前缀
     * @return java.lang.String
     * @author llc
     * @date 2019/5/31 11:03
     */
    private String redisKey(String code) {
        return "oauth:code:" + code;
    }

    /**
     * 重写生成code
     *
     * @param authentication 认证信息
     * @return java.lang.String
     * @author llc
     * @date 2020/1/17 17:47
     */
    @Override
    public String createAuthorizationCode(OAuth2Authentication authentication) {
        String code = GenerateAuthorizationCode.generatorThreadLocal.get().generate();
        store(code, authentication);
        return code;
    }
}
